package com.gridmobi.xrtb.routers.bidding;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.gson.Gson;
import com.gridmobi.xrtb.common.BudgetResourcePool;
import com.gridmobi.xrtb.common.CampaignRateLimiter;
import com.gridmobi.xrtb.domain.dsp.common.ProxyClient;
import com.gridmobi.xrtb.domain.dsp.common.enums.CreativeTypeE;
import com.gridmobi.xrtb.domain.dsp.common.enums.ExchangesE;
import com.gridmobi.xrtb.dspcounter.DspCounterHandler;
import com.gridmobi.xrtb.handler.BiddingLogger;
import com.gridmobi.xrtb.handler.impl.BidHandler;
import com.gridmobi.xrtb.handler.impl.SysError;
import com.gridmobi.xrtb.handler.monitor.SimpleData;
import com.gridmobi.xrtb.model.openrtb23.*;
import com.gridmobi.xrtb.model.rtb.Accumulator;
import com.gridmobi.xrtb.model.rtb.Campaign;
import com.gridmobi.xrtb.util.ChannelHelper;
import com.gridmobi.xrtb.util.RtbUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpStatus;
import org.apache.http.impl.client.CloseableHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.Base64Utils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

@Controller
@RequestMapping("/")
//@Slf4j
public class BiddingController {
//    private static final Logger logger = LoggerFactory.getLogger("com.gridmobi.xrtb.routers.bidding.BiddingController");
    private static final Logger logger = LoggerFactory.getLogger("reqLog");
    private static final Logger errorlogger = LoggerFactory.getLogger("error");

    private static final Logger respLogger = LoggerFactory.getLogger("response");
    private static final String NATIVETYPE = String.valueOf(CreativeTypeE.NATIVE.code);

    private Gson gson = new Gson();
    @Autowired
    private BidHandler bidHandler;

    @Autowired
    DspCounterHandler dspCounterHandler;
    //@Autowired
    //@Qualifier("kafkaBiddingLogger")
    private BiddingLogger biddingLogger;

    @Autowired
    private SysError sysError;


    private AtomicLong count = new AtomicLong(0);
    private AtomicLong count1 = new AtomicLong(0);


    public static CloseableHttpClient client = ProxyClient.getClient();
    private static ListeningExecutorService EXECUTOR_SERVICE = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
    private static Random random = new Random();

    public static void send(InputStream inputStream) {
      /*  int i = random.nextInt(10);
        if (i <= -1) {
            ListenableFuture future = EXECUTOR_SERVICE.submit(() -> {
                try {
                    HttpPost method = new HttpPost("http://34.196.183.175:20080/bid");
                    method.addHeader("Content-type", "application/json; charset=UTF-8");
                    method.setHeader("Accept", "application/json");
                    method.setHeader("Content-Encoding", "gzip");
                    method.setEntity(new InputStreamEntity(inputStream));
                    client.execute(method);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
            try {
                future.get(300, TimeUnit.MILLISECONDS);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        */
        try {
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }


    /**
     * smaato 专用bidder
     *
     * @param request
     * @param response
     * @return
     */

/*

    @PostMapping("bid")
    public void smaatobid(
            HttpServletRequest request,
            HttpServletResponse response) throws Exception {

        */
    /*SMAATO  bid超时设置  美国欧洲250,APAC 600， 中国650 （ms）*//*


        try {

            CompletableFuture future = CompletableFuture.runAsync(() -> {
                try {


                    OpenRTB23BidRequest req = extratReqJson(ExchangesE.SMAATO.key, request, response);
                    if (req == null) {
                        response.sendError(HttpStatus.SC_NO_CONTENT, "No Content");// return 204;
                        return;
                    }


                    OpenRTB23BidResponse resp = doBid(ExchangesE.SMAATO.key, req, request, response);
                    doResponse(ExchangesE.SMAATO.key, request, response, resp, req);


                } catch (Exception e) {
                    Accumulator.errors.incrementAndGet();
                    e.printStackTrace();
                    errorlogger.error("Exception: {}", e.getMessage());
                    try {
                        response.sendError(HttpStatus.SC_NO_CONTENT, "No Content Error");
                    } catch (IOException ex) {
                        ex.printStackTrace();
                    }
                    return;

                }
            });

            future.get(80, TimeUnit.MILLISECONDS);
            if(!response.isCommitted()){
                response.sendError(HttpStatus.SC_NO_CONTENT, "No Content Error");

            }
        } catch (Exception e) {
            if(!response.isCommitted()){
                response.sendError(HttpStatus.SC_NO_CONTENT, "No Content Error");

            }
        }


    }
*/
    @PostMapping("bid")
    public void smaatobid(
            HttpServletRequest request,
            HttpServletResponse response) throws Exception {

        /*SMAATO  bid超时设置  美国欧洲250,APAC 600， 中国650 （ms）*/

        try {


            OpenRTB23BidRequest req = extratReqJson(ExchangesE.SMAATO.key, request, response);
            if (req == null) {
                response.sendError(HttpStatus.SC_NO_CONTENT, "No Content");// return 204;
                return;
            }


            OpenRTB23BidResponse resp = doBid(ExchangesE.SMAATO.key, req, request, response);
            doResponse(ExchangesE.SMAATO.key, request, response, resp, req);


        } catch (Exception e) {
            Accumulator.errors.incrementAndGet();
            e.printStackTrace();
            errorlogger.error("Exception: {}", e.getMessage());
            try {
                response.sendError(HttpStatus.SC_NO_CONTENT, "No Content Error");
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            return;

        }


    }


    @PostMapping("bidy")
    public void bidy(
            HttpServletRequest request,
            HttpServletResponse response) throws Exception {

        /*SMAATO  bid超时设置  美国欧洲250,APAC 600， 中国650 （ms）*/

        try {

            OpenRTB23BidRequest req = extratReqJson(ExchangesE.YEAHMOBI.key, request, response);

            OpenRTB23BidResponse resp = doBid(ExchangesE.YEAHMOBI.key, req, request, response);
            doResponse(ExchangesE.YEAHMOBI.key, request, response, resp, req);

        } catch (Exception e) {
            Accumulator.errors.incrementAndGet();
            e.printStackTrace();
            errorlogger.error("Exception: {}", e.getMessage());
            response.sendError(HttpStatus.SC_NO_CONTENT, "No Content Error");
            return;

        }


    }

    // todo 目前跑的
    @PostMapping("bida")
    public void alxbid(
            HttpServletRequest request,
            HttpServletResponse response) throws Exception {

        /*SMAATO  bid超时设置  美国欧洲250,APAC 600， 中国650 （ms）*/

        try {

            OpenRTB23BidRequest req = extratReqJson(ExchangesE.ALGORIX.key, request, response);
//            logger.info("OpenRTB23BidRequest req = {}", JSON.toJSONString(req));
            OpenRTB23BidResponse resp = doBid(ExchangesE.ALGORIX.key, req, request, response);
            doResponse(ExchangesE.ALGORIX.key, request, response, resp, req);

        } catch (Exception e) {
            Accumulator.errors.incrementAndGet();
            e.printStackTrace();
            errorlogger.error("Exception: {}", e.getMessage());
            response.sendError(HttpStatus.SC_NO_CONTENT, "No Content Error");
            return;

        }


    }

    private OpenRTB23BidResponse doBid(String channel, OpenRTB23BidRequest bidRequest,
                                       HttpServletRequest request,
                                       HttpServletResponse response) {


        if (bidRequest != null) {
            /*Auction type, where 1 = First Price, 2 = Second Price Plus.f
            Exchange-specific auction types can be defined using values greater than 500.*/


            try {
                List<JSONObject> campaigns = bidHandler.matchCampaigns(channel, bidRequest);
                /**
                 * 定向搜索 没有匹配活动
                 */
                List<Campaign> bidPriceCampain = new ArrayList<>();
                if (campaigns != null && campaigns.size() != 0) {
                    Imp imp = bidRequest.getImp()[0];
                    for (JSONObject selectc : campaigns) {
                        Campaign campaign = JSONObject.toJavaObject(selectc, Campaign.class);

                        if(campaign.getBuyingprice() >= imp.getBidfloor()){
                            Float price = BidHandler.calcPrice(campaign, bidRequest);
                            boolean ac = BudgetResourcePool.tryAcquire(campaign.getId() + "", price);
                            if (ac) {
                                campaign.setBidPrice(price);
                                bidPriceCampain.add(campaign);
                            }
                        }
//                        long cid = selectc.getLong("id");
//
//                        String limtkey = cid + bidRequest.getDspTrackLog().getGeo() + bidRequest.getDspTrackLog().getOs();
//                        // System.out.println(limtkey);
//                        if (CampaignRateLimiter.C_LIMITER_SUB.containsKey(limtkey)) {
//                            if (CampaignRateLimiter.C_LIMITER_SUB.get(limtkey).tryAcquire()) {
//                                selectone = true;
//                                break;
//
//                            }
//                        } else if (CampaignRateLimiter.tryAcquire(selectc.getLong("id"))) {
//                            selectone = true;
//                            break;
//                        }

                    }

                    if (CollectionUtils.isEmpty(bidPriceCampain)) {
                        return null;
                    }

                    String country = RtbUtil.get23Country(bidRequest);
                    boolean chn = country.toLowerCase().equals("chn") && channel.equalsIgnoreCase(ExchangesE.ALGORIX.key);
                    Map<String, List<Campaign>> impMatchCampaigns =  bidHandler.matchCreative(channel, bidPriceCampain, bidRequest, campaigns);

                    Map<String, List<Campaign>> targetingCampaigns = bidHandler.targeting(bidRequest, impMatchCampaigns);

                    if (targetingCampaigns.keySet().size() > 0) {
                        Bid[] bids = new Bid[targetingCampaigns.keySet().size()];
                        List<Bid> bidList = new LinkedList();

                        targetingCampaigns.forEach((impid, clist) -> {
                            /*
                             *选举展现活动
                             */
                            Campaign c = bidHandler.electCampaign(clist, bidRequest);
                            /*
                            构建Bid
                             */
                            boolean chnAlg = ChannelHelper.isChnAlg(channel, bidRequest);
                            if (chnAlg) {
                                bidList.add(bidHandler.buildChnBid(impid, channel, c, bidRequest));
                            } else {
                                bidList.add(bidHandler.buildBid(impid, channel, c, bidRequest));
                            }
                            /*
                             *记录BID
                             */

                            bidList.toArray(bids);

                        });

                        if (bids.length == 0 || bids[0] == null || bids[0].getPrice() == 0) {
                            return null;
                        } else if (!(SimpleData.BUDGET_ACTI_CAMPAIGN.contains(bids[0].getCid()) || SimpleData.BUDGET_ACTI_CAMPAIGN.contains(bids[0].getAdid()))) {
                            return null;

                        }
                        if (bids[0].getPrice() == 0) {
                            return null;

                        }

                        OpenRTB23BidResponse bidResponse = new OpenRTB23BidResponse();

                        if (chn) {
                            bidResponse.setId(bidRequest.getId());
                            bidResponse.setCur(null);
                            bidResponse.setSeatbid(new SeatBid[]{bidHandler.buildChnBidSeat(bids, bidRequest)});
                        } else {
                            bidResponse.setCur(BidHandler.BID_CUR_UNIT);
                            bidResponse.setId(bidRequest.getId());
                            bidResponse.setBidid(bidRequest.getId());
                            bidResponse.setSeatbid(new SeatBid[]{bidHandler.buildBidSeat(bids, bidRequest)});
                        }

                        return bidResponse;

                    }

                }


            } catch (Exception e) {
                Accumulator.errors.incrementAndGet();
                e.printStackTrace();
                errorlogger.error("Bid Error:{},{},{}", channel, JSONObject.toJSONString(bidRequest), e);

            }
        }
        return null;

    }

    /**
     * 记录bid log
     * 包含 kafka 以及 本地Log
     */
    public void writeBidLog(String channel, String respJson, OpenRTB23BidRequest req, OpenRTB23BidResponse resp) {

        try {
 /*           ExecutorPool.getExecutor().execute(() -> {
                if (!StringUtils.isEmpty(respJson) && resp != null) {
                    biddingLogger.bidlog(resp, req);

                }
            });*/
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("bidlogerror:", e);
        }

    }

    /**
     * 提取 openRTB JSON 目前是 openRTB 2.3版本
     *
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    public OpenRTB23BidRequest extratReqJson(String channel, HttpServletRequest request,
                                             HttpServletResponse response) throws Exception {

        try {
            /**
             * 如果redis 或者数据同步 error次数超过阈值 停止bid
             */


            String json = null;
            InputStream stream = request.getInputStream();
            if (stream == null) {
                return null;
            }
            String contentEncoding = request.getHeader("Content-Encoding");

            if (!StringUtils.isEmpty(contentEncoding) && contentEncoding.toLowerCase().indexOf("gzip") != -1) {
                stream = new GZIPInputStream(
                        stream, 3096);
            }

            if (stream != null) {
                json = IOUtils.toString(stream, "UTF-8");

                send(stream);

            }

            if (StringUtils.isEmpty(json)) {
                return null;
            }

            OpenRTB23BidRequest req = JSON.parseObject(json, OpenRTB23BidRequest.class);

            return req;
        } catch (Exception e) {
            logger.error(channel, e);
            return null;

        }

    }

    /**
     * 根据bid 结果 响应rtb bid
     *
     * @param request
     * @param response
     * @param resp
     * @param req
     * @throws Exception
     */

    public void doResponse(String channel, HttpServletRequest request,
                           HttpServletResponse response,
                           OpenRTB23BidResponse resp, OpenRTB23BidRequest req) throws Exception {
        String country = RtbUtil.get23Country(req);
        if (country.toLowerCase().equals("chn") && channel.equalsIgnoreCase(ExchangesE.ALGORIX.key)) {
            if (resp == null || resp.getSeatbid() == null || resp.getSeatbid()[0].getBid() == null) {
                if (!response.isCommitted()) {
                    response.sendError(HttpStatus.SC_NO_CONTENT, "No Content");
                }
                return;
            }
            String json = gson.toJson(resp);
            if (StringUtils.isEmpty(json)) {
                response.sendError(HttpStatus.SC_NO_CONTENT, "No Content");
                return;
            }

            response.setStatus(HttpStatus.SC_OK);
            response.setHeader("Content-Type", "application/json;charset=UTF-8");
            response.getWriter().print(json);
            response.flushBuffer();

        } else {
            if (resp == null || resp.getSeatbid() == null
                    || resp.getSeatbid()[0].getBid() == null
                    || resp.getSeatbid()[0].getBid().length == 0
                    || resp.getSeatbid()[0].getBid()[0].getAdomain() == null
                    || resp.getSeatbid()[0].getBid()[0].getAdomain().length == 0) {
                if (!response.isCommitted()) {
                    response.sendError(HttpStatus.SC_NO_CONTENT, "No Content");
                    return;
                }

            } else {

                for (String domain : resp.getSeatbid()[0].getBid()[0].getAdomain()) {
                    if (org.apache.commons.lang3.StringUtils.isBlank(domain)) {
                        response.sendError(HttpStatus.SC_NO_CONTENT, "No Content");
                    }
                }

                String json = gson.toJson(resp);
                if (StringUtils.isEmpty(json)) {
                    response.sendError(HttpStatus.SC_NO_CONTENT, "No Content");
                    return;
                }
   /*         if (random.nextInt(100) == 0) {
                logger.info(json);

            }*/
                String contentEncoding = request.getHeader("Content-Encoding");
                response.setStatus(HttpStatus.SC_OK);
                response.setHeader("Content-Type", "application/json;charset=UTF-8");
                if (!StringUtils.isEmpty(contentEncoding) && contentEncoding.toLowerCase().indexOf("gzip") != -1) {
                    byte[] dataToCompress = json
                            .getBytes(StandardCharsets.UTF_8);
                    ByteArrayOutputStream byteStream =
                            new ByteArrayOutputStream(dataToCompress.length);
                    try {
                        GZIPOutputStream zipStream = new GZIPOutputStream(byteStream, 2048);
                        try {
                            zipStream.write(dataToCompress);
                        } finally {
                            zipStream.close();
                        }
                    } finally {
                        byteStream.close();
                    }
                    response.setHeader("Content-Encoding", "gzip");
                    byteStream.writeTo(response.getOutputStream());
                    response.getOutputStream().flush();
                } else {
                    try {
                        response.getWriter().print(json);
                        response.flushBuffer();
                    } catch (Exception e) {

                    }
                }
                /**
                 * 记录bid log
                 */
                writeBidLog(channel, json, req, resp);

            }
        }


    }

    public static void main(String[] args) {
        System.out.println(new String(Base64Utils.decodeFromString("MDIxODA0NTIwMHwyOTczOTQ0fDB8MHwwfDB8MHwyMTViNTEwOS1kMmZiLTRjMWEtYjVhOS1jODVmZGZjODYyZDI")));
    }
}
